package pl.edu.agh.winmon;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;
import org.hibernate.SessionFactory;
import org.hyperic.sigar.CpuPerc;
import org.hyperic.sigar.Mem;
import org.hyperic.sigar.ProcCpu;
import org.hyperic.sigar.ProcFd;
import org.hyperic.sigar.ProcMem;
import org.hyperic.sigar.ProcState;
import org.hyperic.sigar.ProcUtil;
import org.hyperic.sigar.Sigar;
import org.hyperic.sigar.SigarException;
import org.hyperic.sigar.Uptime;

import pl.edu.agh.winmon.hibernate.CpuInfo;
import pl.edu.agh.winmon.hibernate.CpuLoad;
import pl.edu.agh.winmon.hibernate.MemoryInfo;
import pl.edu.agh.winmon.hibernate.MemoryStatEntry;
import pl.edu.agh.winmon.hibernate.Process;
import pl.edu.agh.winmon.hibernate.ProcessStatEntry;
import pl.edu.agh.winmon.hibernate.Program;
import pl.edu.agh.winmon.hibernate.Session;
import pl.edu.agh.winmon.hibernate.Turn;
import pl.edu.agh.winmon.util.SessionFactoryBuilder;

public class Main {

	private static Logger log = Logger.getLogger(Main.class);

	public static void main(String[] args) {

		SessionFactory sessionFactory = null;
		Map<String, Program> programsMap = new HashMap<String, Program>();
		Map<Long, Process> processesMap = new HashMap<Long, Process>();

		try {
			// Create Sigar handler
			Sigar sigar = new Sigar();

			sessionFactory = SessionFactoryBuilder.getSessionFactory();

			// Create collecting data session
			Session session = new Session();

			// Obtain up date
			Uptime uptime = sigar.getUptime();
			Date nowDate = new Date();
			nowDate.setTime(nowDate.getTime()
					- (long) (uptime.getUptime() * 1000));
			session.setStartTime(nowDate.getTime());

			// Obtain information about CPU
			org.hyperic.sigar.CpuInfo cpu = sigar.getCpuInfoList()[0];

			CpuInfo cpuInfo = new CpuInfo();
			cpuInfo.setCache(cpu.getCacheSize());
			cpuInfo.setCpuCores(cpu.getTotalCores());
			cpuInfo.setCpuModel(cpu.getModel());
			cpuInfo.setCpuSpeed(cpu.getMhz());
			cpuInfo.setCpuVendor(cpu.getVendor());
			cpuInfo.setSession(session);
			session.setCpuInfo(cpuInfo);

			// Obtain information about memory
			Mem memory = sigar.getMem();

			MemoryInfo memoryInfo = new MemoryInfo();
			memoryInfo.setSession(session);
			memoryInfo.setRamMemory(memory.getRam());
			memoryInfo.setTotalMemory(memory.getTotal());
			session.setMemoryInfo(memoryInfo);

			// Save session and cpuInfo
			org.hibernate.Session sess = sessionFactory.getCurrentSession();
			sess.beginTransaction();
			sess.save(session);
			sess.save(cpuInfo);
			sess.save(memoryInfo);
			sess.getTransaction().commit();

			while (true) {
				// Create session for saving
				sess = sessionFactory.getCurrentSession();
				sess.beginTransaction();

				Turn turn = new Turn();
				turn.setSession(session);
				turn.setStartTime(new Date().getTime());

				// Obtain load of each logical CPU
				int i = 1;
				for (CpuPerc cpuPerc : sigar.getCpuPercList()) {
					CpuLoad cpuLoad = new CpuLoad();
					cpuLoad.setCpuLoad(cpuPerc.getUser() + cpuPerc.getSys());
					cpuLoad.setCpuNumber(i);
					cpuLoad.setTurn(turn);
					i++;

					sess.save(cpuLoad);
				}

				// Obtain list of processes
				long[] pids = sigar.getProcList();

				for (Long pid : pids) {
					try {

						String programName = ProcUtil
								.getDescription(sigar, pid);

						Program program = programsMap.get(programName);

						if (program == null) {
							program = new Program();
							program.setSession(session);
							program.setName(programName);

							sess.save(program);
							programsMap.put(programName, program);
						}

						Process process = processesMap.get(pid);
						if (process == null
								|| !process.getProgram().getName()
										.equals(program.getName())) {
							process = new Process();
							process.setPid(pid);
							process.setProgram(program);

							try {
								process.setStartTime(sigar.getProcCpu(pid)
										.getStartTime());
							} catch (SigarException e) {
								// Probably no permission exception
								log.error("Error", e); //$NON-NLS-1$
								e.printStackTrace();
							}

							sess.save(process);
							processesMap.put(pid, process);
						}

						ProcessStatEntry processStatEntry = new ProcessStatEntry();
						processStatEntry.setProcess(process);
						processStatEntry.setTurn(turn);

						try {
							ProcCpu procCpu = sigar.getProcCpu(pid);

							processStatEntry.setLastTime(procCpu.getLastTime());
							processStatEntry.setCpuUsage(procCpu.getPercent());
							processStatEntry.setCpuKernelTime(procCpu.getSys());
							processStatEntry.setCpuUserTime(procCpu.getUser());
							processStatEntry
									.setCpuTotalTime(procCpu.getTotal());
						} catch (SigarException e) {
							// Probably no permission exception
							log.error("Error", e); //$NON-NLS-1$
							e.printStackTrace();
						}

						try {
							ProcFd procFd = sigar.getProcFd(pid);

							processStatEntry.setFileDescriptors(procFd
									.getTotal());
						} catch (SigarException e) {
							// Probably no permission exception
							log.error("Error", e); //$NON-NLS-1$
							e.printStackTrace();
						}

						try {
							ProcMem procMem = sigar.getProcMem(pid);

							processStatEntry.setMemoryMajorFaults(procMem
									.getMajorFaults());
							processStatEntry.setMemoryMinorFaults(procMem
									.getMinorFaults());
							processStatEntry.setMemoryPageFaults(procMem
									.getPageFaults());
							processStatEntry.setTotalResidentMemory(procMem
									.getResident());
							processStatEntry.setTotalSharedMemory(procMem
									.getShare());
							processStatEntry.setTotalVirtualMemory(procMem
									.getSize());
						} catch (SigarException e) {
							// Probably no permission exception
							log.error("Error", e); //$NON-NLS-1$
							e.printStackTrace();
						}

						try {
							ProcState procState = sigar.getProcState(pid);

							processStatEntry.setUsedProcessors(procState
									.getProcessor());
							processStatEntry.setActiveThreads(procState
									.getThreads());
						} catch (SigarException e) {
							// Probably no permission exception
							log.error("Error", e); //$NON-NLS-1$
							e.printStackTrace();
						}

						sess.save(processStatEntry);

					} catch (SigarException e) {
						log.error("Error", e); //$NON-NLS-1$
						e.printStackTrace();
						// Process has gone
					}
				}

				try {
					memory = sigar.getMem();
					MemoryStatEntry memoryStatEntry = new MemoryStatEntry();

					memoryStatEntry.setTurn(turn);
					turn.setMemoryStatEntry(memoryStatEntry);
					memoryStatEntry.setActualFree(memory.getActualFree());
					memoryStatEntry.setActualUsed(memory.getActualUsed());
					memoryStatEntry.setFree(memory.getFree());
					memoryStatEntry.setFreePercent(memory.getFreePercent());
					memoryStatEntry.setUsed(memory.getUsed());
					memoryStatEntry.setUsedPercent(memory.getUsedPercent());

					sess.save(memoryStatEntry);
				} catch (SigarException e) {
					// Probably no permission exception
					log.error("Error", e); //$NON-NLS-1$
					e.printStackTrace();
				}

				sess.save(turn);
				sess.getTransaction().commit();
				Thread.sleep(1000 * 10);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (sessionFactory != null) {
				sessionFactory.close();
			}
		}

	}
}
